from fastapi import FastAPI, HTTPException, Request
from typing import List, Dict, Any
import time
import hashlib
from datetime import datetime, timedelta
import json
import asyncio
from loguru import logger

mock_app = FastAPI(title="Platform Mock Server")

# 模拟数据存储
mock_data = {
    "dahua": {
        "tokens": {},  # 存储token信息
        "subscriptions": {}  # 存储订阅信息
    },
    "hikvision": {
        "tokens": {},
        "subscriptions": {}
    }
}

# 模拟事件类型
MOCK_EVENT_TYPES = {
    "dahua": ["AccessControl", "Face", "Alarm"],
    "hikvision": ["FACE_MATCH", "ACCESS_CONTROL", "ALARM"]
}

# 健康检查接口
@mock_app.get("/health")
async def health_check():
    """健康检查接口"""
    return {
        "status": "healthy",
        "dahua_subscriptions": len(mock_data["dahua"]["subscriptions"]),
        "hikvision_subscriptions": len(mock_data["hikvision"]["subscriptions"])
    }

# 大华平台模拟接口
@mock_app.post("/dahua/oauth/token")
async def dahua_token():
    """模拟大华平台token获取接口"""
    token = f"dahua_mock_token_{int(time.time())}"
    expires_at = datetime.now() + timedelta(hours=2)
    mock_data["dahua"]["tokens"][token] = expires_at
    
    logger.info(f"生成新的token: {token}")
    logger.info(f"当前有效token列表: {list(mock_data['dahua']['tokens'].keys())}")
    
    return {
        "code": 0,
        "msg": "success",
        "data": {
            "accessToken": token,
            "expiresIn": 7200
        }
    }

@mock_app.post("/dahua/eventbus/subscribe")
async def dahua_subscribe(request: Request):
    """模拟大华平台事件订阅接口"""
    data = await request.json()
    subscribe_id = f"dahua_sub_{int(time.time())}"
    mock_data["dahua"]["subscriptions"][subscribe_id] = data
    
    # 启动模拟事件推送
    asyncio.create_task(simulate_dahua_events(data["callbackUrl"], subscribe_id))
    
    return {
        "code": 0,
        "msg": "success",
        "data": {
            "subscribeId": subscribe_id
        }
    }

@mock_app.post("/dahua/eventbus/unsubscribe")
async def dahua_unsubscribe(request: Request):
    """模拟大华平台取消订阅接口"""
    data = await request.json()
    subscribe_id = data.get("subscribeId")
    if subscribe_id in mock_data["dahua"]["subscriptions"]:
        del mock_data["dahua"]["subscriptions"][subscribe_id]
    
    return {
        "code": 0,
        "msg": "success"
    }

# 大华平台门禁控制接口
@mock_app.post("/evo-apigw/evo-accesscontrol/1.0.0/card/accessControl/channelControl/openDoor")
@mock_app.post("/evo-apigw/evo-accesscontrol/1.0.0/card/accessControl/channelControl/closeDoor")
async def dahua_door_control(request: Request):
    """模拟大华平台门禁控制接口"""
    try:
        # 记录请求头
        headers = dict(request.headers)
        logger.info(f"收到门禁控制请求头: {headers}")
        
        # 记录请求体
        data = await request.json()
        logger.info(f"收到门禁控制请求体: {data}")
        
        door_id = data.get("channelCodeList", [None])[0]
        action = "open" if "openDoor" in str(request.url) else "close"
        logger.info(f"门禁控制: door_id={door_id}, action={action}")
        
        # 验证认证头
        auth_header = headers.get("authorization")
        logger.debug(f"认证头: {auth_header}")
        
        if not auth_header:
            logger.error("缺少认证头")
            raise HTTPException(status_code=401, detail="Unauthorized")
            
        # 验证token格式
        if not auth_header.lower().startswith("bearer "):
            logger.error("认证头格式错误")
            raise HTTPException(status_code=401, detail="Invalid token format")
            
        token = auth_header.split(" ")[1]
        logger.debug(f"提取的token: {token}")
        logger.debug(f"当前有效token列表: {list(mock_data['dahua']['tokens'].keys())}")
        
        if token not in mock_data["dahua"]["tokens"]:
            logger.error("无效的token")
            raise HTTPException(status_code=401, detail="Invalid token")
            
        # 验证请求数据
        if not door_id:
            logger.error("缺少门禁ID")
            raise HTTPException(status_code=400, detail="Missing door ID")
            
        logger.info("门禁控制请求验证通过")
        return {
            "code": 0,
            "msg": "success",
            "success": True
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"处理门禁控制请求失败: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

# 海康平台模拟接口
@mock_app.post("/hikvision/oauth/token")
async def hikvision_token():
    """模拟海康平台token获取接口"""
    token = f"hikvision_mock_token_{int(time.time())}"
    expires_at = datetime.now() + timedelta(hours=2)
    mock_data["hikvision"]["tokens"][token] = expires_at
    
    return {
        "code": "0",
        "msg": "success",
        "data": {
            "access_token": token,
            "expires_in": 7200
        }
    }

@mock_app.post("/hikvision/api/v1/events/subscribe")
async def hikvision_subscribe(request: Request):
    """模拟海康平台事件订阅接口"""
    data = await request.json()
    subscribe_id = f"hikvision_sub_{int(time.time())}"
    mock_data["hikvision"]["subscriptions"][subscribe_id] = data
    
    # 启动模拟事件推送
    asyncio.create_task(simulate_hikvision_events(data["callbackUrl"], subscribe_id))
    
    return {
        "code": "0",
        "msg": "success",
        "data": {
            "subscribeId": subscribe_id
        }
    }

@mock_app.post("/hikvision/api/v1/events/unsubscribe")
async def hikvision_unsubscribe(request: Request):
    """模拟海康平台取消订阅接口"""
    data = await request.json()
    subscribe_id = data.get("subscribeId")
    if subscribe_id in mock_data["hikvision"]["subscriptions"]:
        del mock_data["hikvision"]["subscriptions"][subscribe_id]
    
    return {
        "code": "0",
        "msg": "success"
    }

# 模拟事件推送
async def simulate_dahua_events(callback_url: str, subscribe_id: str):
    """模拟大华平台事件推送"""
    import aiohttp
    
    while subscribe_id in mock_data["dahua"]["subscriptions"]:
        try:
            event_type = MOCK_EVENT_TYPES["dahua"][int(time.time()) % 3]
            event_data = {
                "eventType": event_type,
                "eventTime": datetime.now().isoformat(),
                "eventId": f"dahua_event_{int(time.time())}",
                "data": {
                    "deviceId": "mock_device_001",
                    "channelId": "1",
                    "status": "normal"
                }
            }
            
            async with aiohttp.ClientSession() as session:
                async with session.post(callback_url, json=event_data) as response:
                    if response.status == 200:
                        logger.info(f"Successfully pushed Dahua event: {event_type}")
                    else:
                        logger.error(f"Failed to push Dahua event: {response.status}")
        
        except Exception as e:
            logger.error(f"Error pushing Dahua event: {str(e)}")
        
        await asyncio.sleep(10)  # 每10秒推送一次事件

async def simulate_hikvision_events(callback_url: str, subscribe_id: str):
    """模拟海康平台事件推送"""
    import aiohttp
    
    while subscribe_id in mock_data["hikvision"]["subscriptions"]:
        try:
            event_type = MOCK_EVENT_TYPES["hikvision"][int(time.time()) % 3]
            event_data = {
                "eventType": event_type,
                "eventTime": datetime.now().isoformat(),
                "eventId": f"hikvision_event_{int(time.time())}",
                "data": {
                    "deviceId": "mock_device_002",
                    "channelId": "1",
                    "status": "normal"
                }
            }
            
            async with aiohttp.ClientSession() as session:
                async with session.post(callback_url, json=event_data) as response:
                    if response.status == 200:
                        logger.info(f"Successfully pushed Hikvision event: {event_type}")
                    else:
                        logger.error(f"Failed to push Hikvision event: {response.status}")
        
        except Exception as e:
            logger.error(f"Error pushing Hikvision event: {str(e)}")
        
        await asyncio.sleep(10)  # 每10秒推送一次事件

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(mock_app, host="0.0.0.0", port=8002) 